#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "expressions.h"
#include "analyse_chaine.h"
#include "outils.h"
/*
        Description de la fonction
        ==========================-
        Cette foncton teste si le premier caractere du mot renvoyé par la fonction lire_mot est un operateur binaire
*/
int isoperbin(char* str){
        if(str!=NULL){
                char *mot_suiv, *premier_mot = lire_mot(str,&mot_suiv);
                switch(premier_mot[0]){
                        case '+' : return PLUS ;
                        case '-' : return MOINS;
                        case '*' : return MULT;
                        case '/' : return DIV;
                        case '%' : return MOD;
                        case '<' : if(premier_mot[1]=='=')
                                                        return INFE;
                                                else
                                                        return INF ;
                        case '>' : if(premier_mot[1]=='=')
                                                        return SUPE;
                                                else
                                                        return SUP ;
                        case '!' : if (premier_mot[1]=='=')
                                                        return DIFF ;
                                                else break;
                        case '=' : return EG ;
                        case '|' : if (premier_mot[1]=='|')
                                                        return OU ;
                                                else break;
                        case '&' : if (premier_mot[1]=='&')
                                                        return ET ;
                                                else break;
                }
        }
        return -1 ;
}


/*
        Description de la fonction
        ==========================-
        Cette foncton teste si le premier caractere du mot renvoye par la fonction lire_mot() est un operateur unaire
*/
int isoperun(char* str){
        if(str!=NULL){
                char *mot_suiv, *premier_mot = lire_mot(str,&mot_suiv);
                switch(premier_mot[0]){
                        case '!' : if (premier_mot[1]==' ') return NEG ; else break;
                        case '#' : return OPP;
                }
        }
        return -1;
}

/*
        Description de la fonction
        ==========================
        Cette foncton recursive permet la construction de l'arbre d'expressions
*/
Une_expr *construire_expr(Un_prog *pprog,char *str, char **mot_suiv){
        Une_expr *pExpr ,*pExpr1, *pExpr2, *pExpr3;
        pExpr1 =(Une_expr*) allouer(sizeof(Une_expr));
        pExpr2 =(Une_expr*) allouer(sizeof(Une_expr));
        pExpr3 =(Une_expr*) allouer(sizeof(Une_expr));
        int type;
        char *mot_courant;
        mot_courant = lire_mot(str,&(*mot_suiv));
        if(strlen(mot_courant)<1){
                return NULL;
        }
        pExpr = (Une_expr*) allouer(sizeof(Une_expr));

        /*
                Ceci est le cas de base dans lequel l'expression est une valeur entière
                Par exemple : 45
        */
        if(isdigit(*mot_courant)){
                pExpr->type = INT ;             // On met le type à INT
                pExpr->val=atoi(mot_courant);   // on recupere la valeur en la convertissant avec la focion atoi()
                while(isdigit(*mot_courant))mot_courant++;        //on se positionne a la fin
        }

        /*
                Ceci est le cas de base dans lequel lexpression est une variable
                par exemple : v1
        */
        else if(isalpha(*mot_courant)){
                        pExpr->type = VAR;      // on met le type à VAR
                        pExpr->pvar =rechercher_var(pprog,mot_courant); //on recherche la variable
                        if(pExpr->pvar==NULL){  //si on ne trouve pas la variable, rechercher_var() renvoie NULL et donc on affiche une erreur en sortie
                                        printf(" =================== ERREUR ========================\n");
                                        printf(" Erreur rencontr%ce!!! \n Variable %s non definie \n",130, mot_courant);
                                        system("PAUSE");
                                        exit (1);
                        }
                }

        /*
                Cette partie est recursive et construit les deux operandes d'une operateur binaire
                en faisant 2 appels recursifs pour op1 et op2
        */
        else if(isoperbin(mot_courant)!=-1){
                        pExpr->type = OPBIN;
                        pExpr1 = construire_expr(pprog,*mot_suiv,&(*mot_suiv)); //premier appel recursif
                        pExpr2 = construire_expr(pprog,*mot_suiv,&(*mot_suiv)); //deuxieme appel recursif
                        pExpr->poperbin= creer_operbin(isoperbin(mot_courant),pExpr1,pExpr2); //construction de l'opérateur binaire
                }

        /*
                Dans cette partie on contruit un operateur unaire qui n'est composé que d'une operande
                donc qui nécessite un seul appel recursif
        */
         else if(isoperun(mot_courant)!=-1){
                        pExpr->type = OPUN;
                        pExpr3 = construire_expr(pprog,*mot_suiv,&(*mot_suiv));         //appel recursif
                        pExpr->poperun= creer_operun(isoperun(mot_courant),pExpr3);     //constrution de l'opérateur unaire
                }
        else{

                /*
                Cas de Base où lexpression est une constante chaine de caractere
                Par exemple "ma chaine", donc on aura un type STR
                */
                        pExpr->type = STR ;
                        mot_courant++;
                        pExpr->str = dupliquer(mot_courant);
        }
        return pExpr;
}

        /*
                Description de la fonction
                ==========================
                Cette fonction recherche une variable dans pprog et
                Renvoi NULL si la variable n'est pas retrouvée
        */
Une_var *rechercher_var(Un_prog *pprog, char* nom){
        Une_var *v= pprog->pvar;

        //verification de la variable
        if(!VerifCarSpeciaux(nom))
        {
                printf(" =================== ERREUR ========================\n");
                printf(" Erreur rencontr%ce!!! \n Variable %s non valide \n",130, nom);
                system("PAUSE");
                exit(1);
        }
        while(v!=NULL){
                if(strcmp(v->nom_var,nom)==0)
                        return v;
                v = v->suiv;
        }
        return NULL;
}

        /*
                Description de la fonction
                ==========================
                Cette fonction ajoute une variable dans pprog
        */
Une_var* ajouter_var(Un_prog *pprog,char* nom){
        Une_var* nouveau = (Une_var*) allouer(sizeof(Une_var));
        Une_var* premier = pprog->pvar ; // On récupère le pointeur sur l'adresse de la première variable du programme

        // On vérifie si la variable est valide, c'est à dire ne contient pas de caractères spéciaux
        if(!VerifCarSpeciaux(nom))
        {
                printf(" =================== ERREUR ========================\n");
                printf(" Erreur rencontr%ce!!! \n Variable %s non valide \n",130, nom);
                system("PAUSE");
                exit(1);
        }

        // Allocation d'un nouveau maillon
        nouveau->nom_var = dupliquer(nom);
        nouveau->val_var = -1;
        nouveau->suiv = NULL;

        // On retourne le nouveau maillon si liste vide
        if(premier==NULL){
                premier = nouveau;
                return premier;
        }

        // Si la liste n'est pas vide on se met à la fin de la chaine et on ajoute le nouveau maillon
        while(premier->suiv!=NULL){
                premier = premier->suiv ;
        }
        premier->suiv = nouveau;

        // On retourne le premier élément de liste des variables, la tête
        return pprog->pvar ;
}

        /*
                Description de la fonction
                ==========================
                Cette fonction évalue un arbre d'expression et retourne sa valeur
        */
int evaluer_expr(Une_expr *pexpr){
        int a,b;

        // suivant le type de l'expression reçu en argument on teste l'operateur et on retourne le resultat correspondant

        switch(pexpr->type){
                case OPUN : switch(pexpr->poperun->type){
                                case NEG :  return !(evaluer_expr(pexpr->poperun->pexpr));
                                case OPP :  return -1 *(evaluer_expr(pexpr->poperun->pexpr));
                                default : {
                                    printf(" =================== ERREUR ========================\n");
                                    printf(" Erreur rencontr%ce dans l expression \n",130);
                                    system("PAUSE");
                                    exit(1);}
                                }
                case OPBIN : switch(pexpr->poperbin->type){
                                case PLUS : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    return a+b ;
                                case MOINS : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    return a-b ;
                                case MULT : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    return a*b ;
                                case DIV : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    if(b!=0)
                                                    return a/b ;
                                case MOD : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    if(b!=0)
                                                    return a % b ;
                                case EG : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    return a == b ;
                                case INF : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    return a < b ;
                                case SUP : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    return a > b ;
                                case SUPE : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    return a >= b ;
                                case INFE : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    return a <= b ;
                                case ET : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    return a && b ;
                                case OU : a = evaluer_expr(pexpr->poperbin->pexpr1);
                                                    b = evaluer_expr(pexpr->poperbin->pexpr2);
                                                    return a || b ;
                                default : {
                                    printf(" =================== ERREUR ========================\n");
                                    printf(" Erreur rencontr%ce dans l'expression \n",130);
                                    system("PAUSE");
                                    exit(1);}
                                }
                case STR : {
                                printf(" =================== ERREUR ========================\n");
                                printf(" Erreur rencontr%ce dans l'expression \n",130);
                                system("PAUSE");
                                exit(1);}

                case VAR :      return pexpr->pvar->val_var;

                case INT :      return pexpr->val;
        }
}

//============================ALLOCATION DES POINTEURS======================
/*
        Description de la fonction
        ==========================
        Cette fonction alloue un espace mémoire à une expression et initialise son champ type à -1
        Elle retourne un pointeur sur l'expression créée
*/
Une_expr *creer_expr(){

        Une_expr *expr = (Une_expr*) allouer(sizeof(Une_expr));
        expr->type = -1; //initialisation du type à -1
        return expr;
}

/*
        Description de la fonction
        ==========================
        Cette fonction alloue un espace mémoire à un operateur unaire et l'initialise
*/
Un_operun *creer_operun(Type_operun code, Une_expr *pexpr){
        Un_operun *operun = (Un_operun*) allouer(sizeof(Un_operun));
        operun->type = code ;
        operun->pexpr = creer_expr();
        operun->pexpr = pexpr ;
        return operun ;
}


/*
        Description de la fonction
        ==========================
        Cette fonction alloue un espace mémoire à un operateur binaire et l'initialise
*/
Un_operbin *creer_operbin(Type_operbin code, Une_expr *pexpr1,Une_expr *pexpr2){
        Un_operbin *operbin = (Un_operbin*) allouer(sizeof(Un_operbin));
        operbin->type = code ;
        operbin->pexpr2 = creer_expr();
        operbin->pexpr1 = creer_expr();
        operbin->pexpr1 = pexpr1;
        operbin->pexpr2 = pexpr2;
        return operbin ;
}

/*
        Description de la fonction
        ==========================
        Cette fonction alloue un espace mémoire à une instruction et l'initialise
*/
Une_inst *creer_inst(){
        Une_inst *pinst = (Une_inst*) allouer(sizeof(Une_inst));
        pinst->rem_str = NULL;
        pinst->pvar = NULL;
        pinst->pexpr = NULL;
        pinst->branch_lab =NULL;
        pinst->suiv = NULL;
        return pinst;
}

/*
        Description de la fonction
        ==========================
        Cette fonction alloue un espace mémoire à un programme et l'initialise
*/
Un_prog *creer_prog(){
        Un_prog *pprog = (Un_prog*) allouer(sizeof(Un_prog));
        pprog->pinst = NULL;
        pprog->pvar = NULL;
        pprog->plabel = NULL;
        return pprog ;
}
